from multiprocessing import Process
from threading import Thread
from datetime import datetime, timedelta
from config import *
from tool import change_task_status,is_log_outdated
from sql import sqlseverDB
import subprocess, time, json
import psutil

ms = sqlseverDB('{SQL Server}', SQL_SERVER, 'sumaitongpol', SQL_USER, SQL_PWD)

def terminate_task(taskid,jbname='run_task.py'):
    """根据脚本名称关闭对应的 Python 进程"""
    closed = False
    rwname=Task_Name_dict[taskid]
    aff_close=ms.ExecNoQuery('update task set tcount=0,state=0 where [id]=?',(taskid,))
    aff_is_on=ms.ExecNoQuery('update taskStatus set is_on=0 where is_on=1 and task_id=?',(taskid,))
    change_task_status(taskid, 0)  # 将任务状态设为未运行
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        try:
            # 检查是否为目标 Python 进程
            if proc.info['name'] == 'python.exe':
                cmdline=proc.info['cmdline']
                if jbname in cmdline and  str(taskid) in cmdline:
                    
                    children = proc.children(recursive=True)
                    for child in children:
                        try:
                            child.terminate()
                            print(f"已关闭子进程: PID={child.pid}")
                        except psutil.NoSuchProcess:
                            print(f"子进程已不存在: PID={child.pid}")
                        except psutil.AccessDenied:
                            print(f"无法访问子进程: PID={child.pid}")
                        except Exception as e:
                            print(f"关闭子进程 PID={child.pid} 时异常: {e}")

                    # 等待子进程真正退出
                    gone, alive = psutil.wait_procs(children, timeout=5)
                    for p in alive:
                        try:
                            p.kill()
                            print(f"强制杀掉子进程: PID={p.pid}")
                        except Exception as e:
                            print(f"强制杀掉子进程 PID={p.pid} 时异常: {e}")

                    proc.terminate()  # 优雅终止进程

                    try:
                        proc.wait(timeout=5)
                        print(f"已关闭任务:《{rwname}》, 任务ID:{taskid}, 状态: 成功, {aff_close}, 浏览器状态设置:{aff_is_on}")
                    except psutil.TimeoutExpired:
                        proc.kill()
                        print(f"任务超时未关闭，已强制杀掉 PID={proc.pid}")
                    closed = True 
                                     
        except psutil.NoSuchProcess:
            print(f"进程已不存在: {proc.info['pid']}")
        except psutil.AccessDenied:
            print(f"无法访问进程: {proc.info['pid']}")
        except Exception as e:
            print(f"关闭任务《{rwname}》,任务ID:{taskid} 时发生异常: {e}")
    if not closed:
        print(f"未找到与 《{rwname}》,任务ID:{taskid} 相关的任务")

def delay_task(rwid,m):
    time.sleep(m*60)
    rs=ms.ExecQuery('select state from task where [id]=?',(rwid,))
    if rs[0][0]==1:
        ms.ExecNoQuery('UPDATE task SET  state = -1 WHERE [id] = ?', (rwid,))
        time.sleep(30)
    rwname=Task_Name_dict[rwid]
    aff_start = ms.ExecNoQuery('UPDATE task SET tcount = 0, state = 1,LastHouTaiID=null WHERE [id] = ?', (rwid,))
    print(f'启动任务:《{rwname}》, 状态: {aff_start}')

def run_qq_gl(hour):
    while True:
        now = datetime.now()
        
        if now.hour < hour or (now.hour == hour and now.minute == 0 and now.second == 0):
            next_run = now.replace(hour=hour, minute=0, second=0, microsecond=0)
        else:
            next_run = (now + timedelta(days=1)).replace(hour=hour, minute=0, second=0, microsecond=0)
        sleep_time = (next_run - now).total_seconds()
        if sleep_time <= 0:
            print("目标时间已经过期，跳过等待")
            time.sleep(60)
            continue
        
        rwname = '清理病毒目录'
        print(f"距离下次运行任务《{rwname}》还有 {sleep_time / 3600:.2f} 小时")
        command = f'start cmd /c "python 清理病毒目录.py & timeout /t 20"'
        subprocess.run(command, shell=True)
        time.sleep(sleep_time)
        print(f'启动任务:《{rwname}》')

def run_scheduled_task(rwids, hour):
    """每天在指定时间运行程序"""
    while True:
        now = datetime.now()
        
        # 确定目标时间：如果当天的指定时间还未到，设为今天，否则设为明天
        if now.hour < hour or (now.hour == hour and now.minute == 0 and now.second == 0):
            next_run = now.replace(hour=hour, minute=0, second=0, microsecond=0)
        else:
            next_run = (now + timedelta(days=1)).replace(hour=hour, minute=0, second=0, microsecond=0)
        
        # 计算等待时间，并确保为非负数
        sleep_time = (next_run - now).total_seconds()
        if sleep_time <= 0:
            print("目标时间已经过期，跳过等待")
            time.sleep(20)
            continue
        
        rwnames =','.join ([f'《{Task_Name_dict[rwid]}》' for rwid in rwids])
        print(f"{hour}点,需要运行的任务有{rwnames},共 {len(rwids)} 个\n距离下次运行这些任务还有 {sleep_time / 3600:.2f} 小时")

        # 等待到指定时间
        time.sleep(sleep_time)


        for rwid in rwids:

            try:
                rwname=Task_Name_dict[rwid]
                if rwid in [28]:
                    print(f'任务:《{rwname}》,延迟10分钟启动...')
                    Thread(target=delay_task,args=(rwid,10)).start()
                    continue

                if rwid in [3]:
                    print(f'任务:《{rwname}》,延迟5分钟启动...')
                    Thread(target=delay_task,args=(rwid,5)).start()
                    continue

                
                if rwid in [11]:
                    print(f'任务:《{rwname}》,延迟30分钟启动...')
                    Thread(target=delay_task,args=(rwid,30)).start()
                    continue
               
                rs=ms.ExecQuery('select state from task where [id]=?',(rwid,))
                if rs[0][0]==1:
                    ms.ExecNoQuery('UPDATE task SET  state = -1 WHERE [id] = ?', (rwid,))
                    time.sleep(80)
                
                aff_start = ms.ExecNoQuery('UPDATE task SET tcount = 0, state = 1,LastHouTaiID=null WHERE [id] = ?', (rwid,))
                print(f'启动任务:《{rwname}》, 状态: {aff_start}')
            except Exception as e:
                print(f"运行任务时发生错误: {e}")
        time.sleep(60)

def run_cj_kw():

    while True:
   
        rs_word=ms.ExecQuery(f'select count(*) from taskDC where status=0')
        if rs_word[0][0]>0:
            rs_t=ms.ExecQuery('select state from task where id=11')
            cur_state=rs_t[0][0]
            if cur_state==0:
                print(f'检测到有导出任务')
                ms.ExecNoQuery('update task set state=1 where id=11')
            
        time.sleep(10)


def run_check_log():
    """检查日志是否超时"""

    while True:
        time.sleep(300)

        print(f'开始监测在运行任务广告最后日志时间...')

        rs_ttt= ms.ExecQuery(f'select id,logPath from task where state=1 and id not in (4,11,17,18,19,20,22,31)')
        for cur_tid,log_path in rs_ttt:
            try:
                rw_name=Task_Name_dict[cur_tid]
                if is_log_outdated(log_path,20):
                    print(f'任务:《{rw_name}》卡住超过20分钟重启任务')
                    ms.ExecNoQuery('UPDATE task SET  state = -1 WHERE [id] = ?', (cur_tid,))
                    time.sleep(20)
                    aff_start = ms.ExecNoQuery('UPDATE task SET tcount = 0, state = 1 WHERE [id] = ?', (cur_tid,))
                    print(f'任务:《{rw_name}》,重启任务状态:{aff_start}')
            except Exception as e:
                print(f'任务:{cur_tid},监测日志错误 => {e}')

        time.sleep(300)
    

def main(): 

    # 设置定时任务
    rs_tmimg = ms.ExecQuery('SELECT [id],[timing],[mutilTiming] FROM [task] where timing!=-1')
    hhh_dict={}
    for tid,hhh,mutil_hhh in rs_tmimg:
        if mutil_hhh:
            hhh_list=mutil_hhh.split('|')
            if str(hhh) not in hhh_list:
                hhh_list.append(hhh)
        else:
            hhh_list=[str(hhh)]
        
        for thhh in hhh_list:
            if hhh_dict.get(thhh):
                hhh_dict[thhh].append(tid)
            else:
                hhh_dict[thhh]=[tid]

    for hhh,tids in hhh_dict.items():
        scheduled_task_process = Process(target=run_scheduled_task,args=(tids,int(hhh)))
        scheduled_task_process.daemon = True  # 确保主进程退出时子进程自动终止
        scheduled_task_process.start()

    scheduled_task_process = Process(target=run_cj_kw)
    scheduled_task_process.daemon = True  # 确保主进程退出时子进程自动终止
    scheduled_task_process.start()

    scheduled_task_process = Process(target=run_check_log)
    scheduled_task_process.daemon = True  # 确保主进程退出时子进程自动终止
    scheduled_task_process.start()

    scheduled_task_process = Process(target=run_qq_gl,args=(23,))
    scheduled_task_process.daemon = True  # 确保主进程退出时子进程自动终止
    scheduled_task_process.start()

    while True:
        
        with open('task.json', 'r', encoding='utf-8') as f:
            task_json_data = json.load(f)

        rs = ms.ExecQuery('SELECT [id],[state] FROM [task]')

        for taskid, state in rs:
            
            rwname=Task_Name_dict[taskid]
            if state == 1:  # 开启任务
                if not task_json_data.get(rwname):
                    print(f'开启任务: {rwname}')
                    change_task_status(taskid, 1)
  
                    if taskid in (12,13,14,15,16,36):
                        jbname='run_cj_phpro.py'
                    elif taskid in (17,18,19,20,38):
                        jbname='run_deepseek.py'
                    elif taskid in (5,):
                        jbname='run_export_pro.py'
                    else:
                        jbname = 'run_task.py'
                    
                    command = f'start cmd /c "python {jbname} {taskid} & timeout /t 20"'
                    # 使用subprocess来执行命令
                    subprocess.run(command, shell=True)

            elif state == -1:  # 关闭任务

                if taskid in (12,13,14,15,16,36):
                    jbname='run_cj_phpro.py'
                elif taskid in (17,18,19,20,38):
                        jbname='run_deepseek.py'
                elif taskid in (5,):
                        jbname='run_export_pro.py'
                else:
                    jbname = 'run_task.py'
                print(f'关闭任务: {rwname}')
                terminate_task(taskid,jbname)
        ms.ExecNoQuery("update errbid set iscl=1 where iscl=0 and bname like '%志伟%' ")
        now=datetime.now()
        print(f'当前时间:{now},等待20s,开启下一轮任务监测')
        time.sleep(20)

if __name__ == "__main__":
    main()
